﻿using NServiceKit.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace HobSoft.SoccerBet2014.Domain.Implementation.RedisRepository
{
    using Core;
    using IRepositories;
    using Models;
    using Models.KeyHelper;
    using SoccerBet2014.Common.Queryable;

    public class GameRepository : ReadWriteRedisRepository<Game>, IGameRepository
    {
        protected static string PRE_GAME_ODDS_ID = "urn:odds:list[{0}]";
        public GameRepository(PooledRedisClientManager pooledRedisClient)
            : base(pooledRedisClient)
        {

        }

        public override bool Delete(Game model)
        {
            if (model == null) return false;
            using (var redisClient = CreateRedisClient())
            {
                using (var redisModel = redisClient.As<Game>())
                {
                    redisModel.Delete(model);
                    redisModel.DeleteRelatedEntities<Odds>(model.Key);
                    redisModel.DeleteRelatedEntities<Market>(model.Key);
                }
            }
            return true;
        }

        public override bool Delete(params object[] id)
        {
            if (id.Length < 1) throw new ArgumentNullException("need id");
            var key = id[0];
            using (var redisClient = CreateRedisClient())
            {
                using (var redisModel = redisClient.As<Game>())
                {
                    redisModel.DeleteById(key);
                    redisModel.DeleteRelatedEntities<Odds>(key);
                    redisModel.DeleteRelatedEntities<Market>(key);
                }
            }
            return true;
        }

        public override int DeleteMany(Expression<Func<Game, bool>> predicate)
        {
            if (predicate == null)
                throw new ArgumentNullException("predicate", "need predicate");
            int count = 0;
            using (var redisClient = CreateRedisClient())
            {
                using (var redisModel = redisClient.As<Game>())
                {
                    var models = redisModel.GetAll().AsQueryable().Where(predicate);
                    count = models.Count();
                    if (count == 0) return 0;
                    foreach (var mod in models)
                    {
                        redisModel.Delete(mod);
                        redisModel.DeleteRelatedEntities<Odds>(mod.Key);
                        redisModel.DeleteRelatedEntities<Market>(mod.Key);
                    }
                }
            }
            return count;
        }

        public virtual int Finish(byte bookerId, params string[] ids)
        {
            if (!ids.Any()) return 0;
            int resultCount = 0;
            using (var redisGame = CreateRedisClient<Game>())
            {
                try
                {
                    var keys = ids.Select(i => KeyGenerateHelper.Create<Game>(bookerId, i));
                    var games = redisGame.GetByIds(keys);
                    if (games.Any())
                    {
                        foreach (var gm in games)
                        {
                            gm.State = Game.GameState.Finish;
                            gm.LastUpdateTime = DateTime.Now;
                        }
                        redisGame.StoreAll(games);
                        resultCount = games.Count;
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    redisGame.RedisClient.Dispose();
                }
            }
            return resultCount;
        }

        public virtual bool BackupPreGame(byte bookerId,string id)
        {
            using (var redisClient = CreateRedisClient())
            {
                var redisGame = redisClient.As<Game>();
                var redisOdds = redisClient.As<Odds>();
                var key = KeyGenerateHelper.Create<Game>(bookerId, id);
                var game = redisGame.GetById(key);
                if (game == null) return false;
                if (game.State == Game.GameState.PreGame)
                {
                    var oddsRange = redisGame.GetRelatedEntities<Odds>(id);
                    if (oddsRange == null || oddsRange.Count == 0) return false;
                    var preGameOddsID = string.Format(PRE_GAME_ODDS_ID, id);
                    var list = redisOdds.Lists[preGameOddsID];
                    list.Clear();
                    using (var tran = redisOdds.CreateTransaction())
                    {
                        foreach (var odds in oddsRange)
                        {
                            tran.QueueCommand(ro => ro.AddItemToList(list, odds));
                        }
                        tran.Commit();
                    }
                }
            }
            return true;
        }


        public override int DeleteMany(params object[] keys)
        {
            if (keys == null)
                throw new ArgumentNullException("need keys");
            if (!keys.Any())
                return 0;
            int resultCount = keys.Count();
            using (var redisGame = CreateRedisClient<Game>())
            {
                try
                {
                    using (var tran = redisGame.CreateTransaction())
                    {
                        tran.QueueCommand(rg => rg.DeleteByIds(keys));
                        foreach (var gid in keys)
                        {
                            tran.QueueCommand(rg => rg.DeleteRelatedEntities<Odds>(gid));
                            tran.QueueCommand(rg => rg.DeleteRelatedEntities<Market>(gid));
                        }
                        tran.Commit();
                        tran.Dispose();
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    redisGame.RedisClient.Dispose();
                }
            }
            return resultCount;
        }

        public int UpdateRelatedEntities(IList<Game> games, IList<Market> marketRange, IList<Odds> oddsRange)
        {
            if (games == null)
                throw new ArgumentNullException("need games");
            if (!games.Any()) return 0;

            using (var redisGame = CreateRedisClient<Game>())
            {
                try
                {
                    redisGame.StoreAll(games);
                    foreach (var gm in games)
                    {
                        var refMk = marketRange.Where(m => m.GameID == gm.GameID);
                        var refOdds = oddsRange.Where(o => o.GameID == gm.GameID);
                        if (refMk.Any())
                            redisGame.StoreRelatedEntities(gm.Key, refMk.ToArray());
                        if (refOdds.Any())
                            redisGame.StoreRelatedEntities(gm.Key, refOdds.ToArray());
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    redisGame.RedisClient.Dispose();
                }
            }
            return games.Count;
        }

        public void ClearNoRelated()
        {
            using (var redisClient = CreateRedisClient())
            {
                var redisOdds = redisClient.As<Odds>();
                var redisGame = redisClient.As<Game>();
                var redisMarket = redisClient.As<Market>();

                var allOdds = redisOdds.GetAll();
                List<Game> games = new List<Game>();
                foreach (var odds in allOdds)
                {
                    if (games.Any(g => g.BmID == odds.BmID && g.GameID == odds.GameID))
                        continue;
                    var gm = redisGame.GetById(KeyGenerateHelper.Create<Game>(odds.BmID, odds.GameID));
                    if (gm == null)
                        redisOdds.DeleteById(odds.Key);
                    else
                        games.Add(gm);
                }
                var allMarket = redisMarket.GetAll();
                foreach (var mk in allMarket)
                {
                    if (games.Any(g => g.BmID == mk.BmID && g.GameID == mk.GameID))
                        continue;
                    var gm = redisGame.GetById(KeyGenerateHelper.Create<Game>(mk.BmID, mk.GameID));
                    if (gm == null)
                        redisMarket.DeleteById(mk.Key);
                    else
                        games.Add(gm);
                }
            }
        }
    }
}
